<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>Stockfish.js Tester</title>
  <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, user-scalable=no" />
  <link rel="icon" href="favicon.svg">
  <style>
    * {
        box-sizing: border-box;
    }
    html, body {
        height: 100%;
        margin: 0 3px;
    }
    table {
        height: 100%;
        width: 100%;
    }
    .tr2 {
        height: 20px;
        width: 100%;
    }
    #input {
        width: 100%;
    }
    textarea {
        width: 100%;
        height: 100%;
    }
    #selection {
        width: 10em;
    }
  </style>
</head>
<script>
/// This can be useful for debugging on a tablet.
/*
window.onerror = function (e)
{
    alert("onerror: " + e)
}
console.log = function (a)
{
    alert("log: "+  a)
}
console.warn = function (a)
{
    alert("warn: " + a)
}
console.error = function (a)
{
    alert("error: " + a)
}
console.info = function (a)
{
    alert("info: " + a)
}
//*/
</script>
<body>
  <table>
  <tr><td colspan=3>
    <textarea id=output autocorrect=off autocapitalize=none spellcheck=false></textarea>
  </td></tr>
  <tr><td class=tr2>
    <input id=input type=text autocorrect=off autocapitalize=none spellcheck=false>
  </td><td>
    <input type=button id=send value=Send>
  </td><td>
    <select id=selection title=Examples></select>
  </td></tr>
  </table>
</body>
<script src=loadEngine.js></script>
<script>
    function getParams()
    {
        var params = {};
        var sep1 = location.search.split(/\&|\?/g);
        var sep2;
        var i;
        var len;
        
        len = sep1.length;
        
        if (len > 1) {
            ///NOTE: Skip the first empty element (it's empty because URL's start with a slash).
            for (i = 1; i < len; i += 1) {
                sep2 = sep1[i].split(/=/);
                sep2[0] = decodeURIComponent(sep2[0].replace(/\+/g, " "));
                if (sep2[1]) {
                    sep2[1] = decodeURIComponent(sep2[1]).replace(/\+/g, " ");
                    
                    /// Convert numbers to Numbers.
                    if (+sep2[1] === +sep2[1]) {
                        sep2[1] = +sep2[1];
                    } else if (sep2[1] === "false") {
                        sep2[1] = false;
                    } else if (sep2[1] === "true") {
                        sep2[1] = true;
                    }
                } else {
                    sep2[1] = true;
                }
                if (typeof params[sep2[0]] === "undefined") {
                    params[sep2[0]] = sep2[1];
                } else {
                    if (typeof params[sep2[0]] !== "object") {
                        params[sep2[0]] = [params[sep2[0]]];
                    }
                    params[sep2[0]].push(sep2[1]);
                }
            }
        }
        
        return params;
    }
        
    function getConfig(key, defaultVal)
    {
        var val;
        try {
            val = localStorage.getItem(key);
            if (val !== null && typeof val !== "undefined") {
                return JSON.parse(val);
            }
        } catch (e) {}
        return defaultVal;
    }
    
    function setConfig(key, value)
    {
        try {
            return localStorage.setItem(key, JSON.stringify(value));
        } catch (e) {}
    }
    
    
    var params = getParams();
    var engineBasename = params.engine && typeof params.engine === "string" ? params.engine.replace(/\.(?:js|wasm)$/, "") : "stockfish";
    var input = document.getElementById("input");
    var output = document.getElementById("output");
    var first = true;
    var lastCmds = getConfig("lastCmds", []);
    var lastCmdsIndex = lastCmds.length;
    var tmpCmd = getConfig("tmpCmd", "");
    
    var engine = loadEngine("/src/" + engineBasename + ".js#/src/" + engineBasename + ".wasm", function ()
    {
        /// Engine is running.
        console.log("__up__")
    });
    function writeOutput(data)
    {
        if (first) {
            first = false;
        } else {
            output.value += "\n";
        }
        output.value += data;
        output.scrollTop = output.scrollHeight;
    }

    function removeDupsRev(arr)
    {
        var unique = {};
        var i;
        var len = arr.length;
        var dups = {};
        
        for (i = len - 1; i >= 0; --i) {
            if (!unique[arr[i]]) {
            unique[arr[i]] = 1;
            } else {
                dups[arr[i]] = 1;
                arr.splice(i, 1);
                //++i;
            }
        }
        return arr;
    }

    function sendCommand(cmd, skipSave)
    {
        var match;
        cmd = cmd.trim();
        if (cmd) {
            /// Supports multiple commands separated by semicolons
            if (cmd.indexOf(";") > -1) {
                cmd.split(";").forEach(function (part)
                {
                    sendCommand(part, true);
                });
            } else {
                writeOutput(" > " + cmd);
                /// Supports delayed commands by wrapping in a sleep function, like setTimeout: sleep(CMD, DELAY_MS)
                match = cmd.match(/^sleep\(\s*(.+)\s*,\s*(\d+)\s*\)$/);
                if (match) {
                    setTimeout(function ()
                    {
                        sendCommand(match[1], true);
                    }, Number(match[2]));
                } else {
                    engine.send(cmd);
                }
            }
            if (!skipSave) {
                lastCmds.push(cmd);
                removeDupsRev(lastCmds);
                if (lastCmds.length > 100) {
                    lastCmds = lastCmds.slice(-80);
                }
                setConfig("lastCmds", lastCmds);
                lastCmdsIndex = lastCmds.length;
            }
        }
    }
    engine.stream = writeOutput;
    
    function setTmpCmd(val)
    {
        tmpCmd = val;
        setConfig("tmpCmd", val);
    }
    
    function cursorToEnd(el)
    {
        setTimeout(function ()
        {
            el.selectionStart = el.selectionEnd = el.value.length;
        }, 1);
    }
    
    input.onkeydown = function keypress(e)
    {
        if (e.key === "Enter") {
            sendCommand(input.value);
            input.value = "";
            setTmpCmd("");
        } else if (e.key === "ArrowUp") {
            if (lastCmdsIndex > 0) {
                if (lastCmdsIndex === lastCmds.length) {
                    setTmpCmd(input.value);
                }
                --lastCmdsIndex;
                input.value = lastCmds[lastCmdsIndex];
                cursorToEnd(input);
            }
        } else if (e.key === "ArrowDown") {
            if (lastCmdsIndex < lastCmds.length - 1) {
                ++lastCmdsIndex;
                input.value = lastCmds[lastCmdsIndex];
                cursorToEnd(input);
            } else if (lastCmdsIndex === lastCmds.length - 1) {
                ++lastCmdsIndex;
                input.value = tmpCmd;
                cursorToEnd(input);
            }
        } else {
            if (lastCmdsIndex === lastCmds.length) {
                setTmpCmd(input.value);
            }
        }
    };
    document.getElementById("send").onclick = function ()
    {
        sendCommand(input.value);
        input.value = "";
        setTmpCmd("");
        input.focus();
    };
    function setupSamples()
    {
        var selectBox = document.getElementById("selection");
        [
            "go",
            "stop",
            "uci",
            "isready",
            "ucinewgame",
            "go depth 10",
            "go depth 20",
            "go depth 25",
            //"bench",
            "d",
            "eval",
            "go;sleep(stop,1000)",
            "position startpos",
            "position fen r1k4r/ppp1bq1p/2n1N3/6B1/3p2Q1/8/PPP2PPP/R5K1 w - - 0 1;d",
            "position fen 8/7p/6p1/5p2/Q4P2/2p3P1/3r3P/2K1k3 w - - 2 44 moves a4a7;d",
            "setoption name Threads value 1",
            "setoption name Threads value 4",
            "setoption name MultiPV value 1",
            "setoption name MultiPV value 3",
            "setoption name Clear Hash",
            "setoption name Clear Hash;ucinewgame",
            "go movetime 1000",
            "go wtime 2000 btime 2000",
            "go nodes 30000",
            "go ponder",
            "ponderhit",
        ].forEach(function (cmd)
        {
            var el = document.createElement("option");
            el.value = el.textContent = cmd;
            selectBox.appendChild(el);
        });
        selectBox.selectedIndex = -1;
        selectBox.oninput = function ()
        {
            if (selectBox.value) {
                sendCommand(selectBox.value);
                selectBox.selectedIndex = -1;
            }
        };
    }
    setupSamples();
    input.value = tmpCmd;
    input.select();
</script>
</html>
